package task3.pimpmypipe;

import java.io.StreamCorruptedException;
import java.security.InvalidParameterException;

public abstract class AbstractFilter<in, out>
  implements IOable<in, out>, Runnable
{
  private Readable<in> m_Input = null;
  private Writeable<out> m_Output = null;
  public static Object ENDING_SIGNAL = null;
  
  public AbstractFilter() {}
  
  public AbstractFilter(Readable<in> input)
    throws InvalidParameterException
  {
    if (input == null) {
      throw new InvalidParameterException("input can't be null!");
    }
    this.m_Input = input;
  }
  
  public AbstractFilter(Writeable<out> output)
    throws InvalidParameterException
  {
    if (output == null) {
      throw new InvalidParameterException("output can't be null!");
    }
    this.m_Output = output;
  }
  
  public AbstractFilter(Readable<in> input, Writeable<out> output)
    throws InvalidParameterException
  {
    if (input == null) {
      throw new InvalidParameterException("input can't be null!");
    }
    if (output == null) {
      throw new InvalidParameterException("output can't be null!");
    }
    this.m_Input = input;
    this.m_Output = output;
  }
  
  protected void writeOutput(out value)
    throws StreamCorruptedException
  {
    if (this.m_Output != null)
    {
      if (value == ENDING_SIGNAL) {
        beforeSendingEndingSignal();
      }
      this.m_Output.write(value);
    }
    else
    {
      throw new StreamCorruptedException("output is null");
    }
  }
  
  protected in readInput()
    throws StreamCorruptedException
  {
    if (this.m_Input != null) {
      return this.m_Input.read();
    }
    throw new StreamCorruptedException("input is null");
  }
  
  protected void sendEndSignal()
    throws StreamCorruptedException
  {
    writeOutput(null);
  }
  
  protected void beforeSendingEndingSignal()
    throws StreamCorruptedException
  {}
  
  public void setInput(Readable<in> input)
  {
    this.m_Input = input;
  }
  
  public void setOutput(Writeable<out> output)
  {
    this.m_Output = output;
  }
}
